package Optimizer;

import Engine.src.ca.uqac.dim.turtledb.*;
import java.util.ArrayList;

public class QueryTranslator {

    public static Relation translate(String q) {
        try {
                ArrayList<String> selectElts = segmentQuery(q, "SELECT", "FROM", ",");		
                ArrayList<String> fromElts = segmentQuery(q, "FROM", "WHERE", ",");
                ArrayList<String> whereElts = segmentQuery(q, "WHERE", ";", "AND");

                // Etape: NORMALISATION
                // La clause WHERE est dejà sous la forme conjonctive
                
                // Etape: ANALYSE
                
                // Suppression des attributs projetés incorrect
                for(int i=0; i<selectElts.size(); i++){
                    boolean isOk = false;
                    
                    for(String from : fromElts){
                        if(selectElts.get(i).startsWith(from)){
                            isOk = true;
                        }
                    }
                    
                    if(!isOk) selectElts.remove(selectElts.get(i));       
                }
                
                // Suppression des tables non utilisées
                for(int i=0; i<fromElts.size(); i++){
                    boolean isUsed = false;
                    
                    for(String select : selectElts){
                        if(select.startsWith(fromElts.get(i))){
                            isUsed = true;
                        }
                    }
                    
                    if(!isUsed){
                        for(String where : whereElts){
                            if(where.contains(fromElts.get(i))){
                                for(String from : fromElts){
                                    if(!from.equals(fromElts.get(i))){
                                       if(where.contains(from)){
                                            // Alors c'est une jointure
                                            isUsed = true;
                                        }
                                        // Sinon c'est une selection sur la table qui n'est pas utile pour le resultat
                                    }
                                }
                            }
                        }
                    }
                    
                    if(!isUsed) fromElts.remove(i);
                }
                
                // Suppression des contraintes incorrect
                for(int i=0; i<whereElts.size(); i++){
                    boolean isOk = false;
                    
                    for(String from : fromElts){
                        if(whereElts.get(i).contains(from)){
                            isOk = true;
                        }
                    }
                    
                    if(!isOk) whereElts.remove(i);
                }
                
                // Etape: SIMPLIFICATION
                
                // p AND p = p
                for(int i=0; i<whereElts.size(); i++){
                    for(int j=0; j<whereElts.size(); j++){
                        if(i != j){
                            if(whereElts.get(i).compareTo(whereElts.get(j)) == 0){
                                whereElts.remove(j);
                            }
                        }
                    }
                    
                }
                
                // CONSTRUCTION DE L'ARBRE
                
                Schema sch = new Schema();
                for(String select : selectElts){
                    sch.add(new Attribute(select));       
                }
                
                Product product = null;
                VariableTable table = null;
                     
                if(fromElts.size() > 1){
                    product = new Product();
                    
                    for(String from : fromElts){			
                        product.addOperand(new VariableTable(from));
                    }
                }
                else if(fromElts.size() == 1){
                    table = new VariableTable(fromElts.get(0));
                }
                        
                Condition cond = createCondition(whereElts, fromElts);

                Selection selection = null;
                if(cond != null){
                    if(product != null){
                        selection = new Selection(cond, product);
                    }
                    else{
                        selection = new Selection(cond, table);   
                    }
                }

                Projection projection = null;
                if(selection == null){
                    if(product != null){
                        projection = new Projection(sch, product);
                    }
                    else{
                        projection = new Projection(sch, table);
                    }
                        
                }
                else{
                        projection = new Projection(sch, selection);
                }

                return projection;
        } catch (QueryTranslatorException e) {
                System.err.println(e);
                return null;
        }
    }

    private static Equality createEquality(String s, ArrayList<String> tables){
        Equality equal = null;

        String[] elts = s.split("=");
        if(elts.length == 2){
            boolean isAttribute = false;
            
            for(String table : tables){
                if(elts[1].trim().startsWith(table)) isAttribute = true;
            }
            
            if(isAttribute){
                equal = new Equality(new Attribute(elts[0].trim()), new Attribute(elts[1].trim()));
            }
            else{
                equal = new Equality(new Attribute(elts[0].trim()), new Value(elts[1].trim()));     
            }
        }

        return equal;
    }

    private static LogicalOr createLogicalOr(String s, ArrayList<String> tables){
        LogicalOr or = null;

        String str = s;
        if(s.startsWith("(") && s.endsWith(")")){
                str = s.substring(1, s.length()-1);
        }

        String[] elts = str.split("OR");

        if(elts.length > 0){
                or = new LogicalOr();

                for(String elt : elts){
                        Equality cond = createEquality(elt, tables);

                        if(cond != null){
                                or.addCondition(cond);
                        }
                }		
        }		

        return or;
    }

    private static Condition createCondition(ArrayList<String> whereElts, ArrayList<String> fromElts){
        Condition cond = null;

        if(!whereElts.isEmpty()){
                if(whereElts.size() == 1){
                        if(whereElts.get(0).contains("OR")){
                                cond = createLogicalOr(whereElts.get(0), fromElts);
                        }
                        else{
                                cond = createEquality(whereElts.get(0), fromElts);
                        }
                }
                else{
                        cond = new LogicalAnd();

                        for(String elt : whereElts){
                                if(elt.contains("OR") && elt.startsWith("(") && elt.endsWith(")")){
                                    ((NAryCondition) cond).addCondition(createLogicalOr(elt, fromElts));
                                }
                                else{
                                    ((NAryCondition) cond).addCondition(createEquality(elt, fromElts));
                                }
                        }
                }
        }

        return cond;
    }

    private static ArrayList<String> segmentQuery(String query, String start, String end, String symbol)
                    throws QueryTranslatorException {
        if(symbol.isEmpty()){
                throw new QueryTranslatorException("Erreur paramètre symbole de segmentation");
        }

        int startIndex = query.toLowerCase().indexOf(start.toLowerCase());
        int endIndex = query.toLowerCase().indexOf(end.toLowerCase());

        if(startIndex == -1 || endIndex == -1) {
                throw new QueryTranslatorException("Impossible de segmenter la requête");
        }

        startIndex += start.length();
        ArrayList<String> segments = new ArrayList<String>();

        for ( String segment : query.substring(startIndex, endIndex).split(symbol) ) {
                if(!segment.trim().isEmpty()){
                        segments.add(segment.trim());	
                }
                else{
                        throw new QueryTranslatorException("Format de la requête incorrect");
                }
        }

        return segments;
    }	
}

class QueryTranslatorException extends Exception {

    public QueryTranslatorException(){
        super();
    }

    public QueryTranslatorException(String s){
        super(s);
    }

}
